#ifndef NDKPLAYER_SAFEQUEUE_H
#define NDKPLAYER_SAFEQUEUE_H

#include <queue>
#include <pthread.h>

using namespace std;

/**
 * 线程安全队列
 * @tparam T  泛型：存放任意类型
 */
template<typename T>
class SafeQueue {
private:
    typedef void (*ReleaseCallback)(T *); // 函数指针定义 做回调 用来释放T里面的内容的
    typedef void (*SyncCallback)(queue<T> &); // 函数指针定义 做回调 让外界完成丢包动作
private:
    queue<T> queue;
    pthread_mutex_t mutex; // 互斥锁 安全
    pthread_cond_t cond; // 等待 和 唤醒
    int work; // 标记队列是否工作
    ReleaseCallback releaseCallback;
    // TODO 4.音视频同步
    SyncCallback syncCallback;
public:
    SafeQueue() {
        pthread_mutex_init(&mutex, 0); // 初始化互斥锁
        pthread_cond_init(&cond, 0); // 初始化条件变量
    }

    virtual ~SafeQueue() {
        pthread_mutex_destroy(&mutex); // 释放互斥锁
        pthread_cond_destroy(&cond); // 释放条件变量
    }

    /**
     * 入队
     * @param value
     */
    void push(T value) {
        pthread_mutex_lock(&mutex); // 多线程的访问（先锁住）
        if (work) {
            // 工作状态，入队
            queue.push(value);
            // 当插入数据包 进队列后，发出通知唤醒
            pthread_cond_signal(&cond);
        } else {
            //非工作状态，释放value
            if (releaseCallback) {
                releaseCallback(&value);
            }
        }
        pthread_mutex_unlock(&mutex); // 多线程的访问（要解锁）
    }

    /**
     * 出队
     * @param value
     * @return
     */
    int pop(T &value) {
        int result = 0;
        pthread_mutex_lock(&mutex); // 多线程的访问（先锁住）
        while (work && queue.empty()) {
            // 如果是工作 并且 队列里面没有数据，就阻塞在这里
            pthread_cond_wait(&cond, &mutex);
        }
        if (!queue.empty()) {
            // 取出队列的数据包 给外界，并删除队列数据包
            value = queue.front();
            // 删除队列中的数据
            queue.pop();
            // 成功 return true
            result = 1;
        }
        pthread_mutex_unlock(&mutex); // 多线程的访问（要解锁）

        return result;
    }

    /**
     * 设置工作状态，设置队列是否工作
     * @param work
     */
    void setWork(int work) {
        pthread_mutex_lock(&mutex); // 多线程的访问（先锁住）
        this->work = work;
        // 每次设置状态后，就去唤醒
        pthread_cond_signal(&cond);
        pthread_mutex_unlock(&mutex); // 多线程的访问（要解锁）
    }

    int empty() {
        return queue.empty();
    }

    int size() {
        return queue.size();
    }

    /**
     * 清空队列中所有的数据，循环一个一个的删除
     */
    void clear() {
        pthread_mutex_lock(&mutex); // 多线程的访问（先锁住）
        unsigned int size = queue.size();
        for (int i = 0; i < size; ++i) {
            //循环释放队列中的数据
            T value = queue.front();
            if (releaseCallback) {
                releaseCallback(&value); // 让外界去释放堆区空间
            }
            queue.pop(); // 删除队列中的数据，让队列为0
        }
        pthread_mutex_unlock(&mutex); // 多线程的访问（要解锁）
    }

    /**
     * 设置此函数指针的回调，让外界去释放
     * @param releaseCallback
     */
    void setReleaseCallback(ReleaseCallback releaseCallback) {
        this->releaseCallback = releaseCallback;
    }

    /**
    * 设置此函数指针的回调，让外界去丢包
    * @param syncCallback
    */
    void setSyncCallback(SyncCallback syncCallback) {
        this->syncCallback = syncCallback;
    }

    /**
     * 同步操作 丢包
     */
    void sync() {
        pthread_mutex_lock(&mutex);
        syncCallback(queue); // 函数指针 具体丢包动作，让外界完成
        pthread_mutex_unlock(&mutex);
    }
};

#endif //NDKPLAYER_SAFEQUEUE_H
